
import { createWorker, createWorkerFunc, closeWorker } from './utils/versionPolling.js';

let APP_ETAG_KEY = "__APP_ETAT__";
let myWorker;
/* VersionPollingOptions: {
  appETagKey?: string; // web 应用更新唯一标识字段名
  pollingInterval?: number; // 轮询间隔，单位为毫秒，默认为 5 分钟
  htmlFileUrl?: string; // web 应用网站运行目录
  silent?: boolean; // 安静模式，为true时，不会进行实时监测
  silentPollingInterval?: boolean; // 安静模式，为true时，不做轮询任务
  silentPageVisibility?: boolean; // 安静模式，为true时，不做页面可见状态监听
  forceUpdate?: boolean; // 强制更新，为true是，取消检查都会更新
  onUpdate?: (data: any) => void; // 更新检测的回调函数，可以自定义更新逻辑
} */
const defaultOptions = {
  appETagKey: APP_ETAG_KEY, // web 应用更新唯一标识字段名
  pollingInterval: 5 * 60 * 1000, // 轮询间隔，单位为毫秒，默认为 5 分钟
  htmlFileUrl: `${location.origin}${location.pathname}`, // web 应用网站运行目录
  silent: false, // 安静模式，为true时，不会进行实时监测
  silentPollingInterval: false, // 安静模式，为true时，不做轮询任务
  silentPageVisibility: false, // 安静模式，为true时，不做页面可见状态监听
  forceUpdate: false, // 强制更新，为true是，取消检查都会更新
}
// 标识是否注册页面可见性visibilitychange事件
let attached = false;

/**
 * 页面隐藏时停止轮询任务，页面再度可见时再继续
 */
const handleVisibilityChange = () => {
  if (document.visibilityState === "visible") {
    myWorker.postMessage({
      code: "resume",
    })
  } else {
    myWorker.postMessage({
      code: "pause"
    })
  }
}

/**
 * VersionPolling是一个用于版本轮询的类，它通过定时请求服务器来检查是否有新的版本更新。如果检测到有新版本，会执行回调函数进行更新操作。
 * @param {VersionPollingOptions} options - 配置项，包括appETagKey（应用的ETag键）、pollingInterval（轮询间隔时间）、htmlFileUrl（HTML文件的URL）、silent（是否静默模式）、silentPollingInterval（是否静默轮询间隔）、silentPageVisibility（是否静默页面可见性）、forceUpdate（是否强制更新）和onUpdate（更新时的回调函数）。
 */
class VersionPolling {
  options;
  appEtag = "";

  constructor(options) {
    this.options = Object.assign({}, defaultOptions, options);
    this.init();
  }

  /**
   * init()初始化方法，获取初始ETag并开始轮询任务。
   * @async
   * @throws {Error} 如果htmlFileUrl为空或者请求状态不为200，会抛出错误。
   */
  async init() {
    const { htmlFileUrl } = this.options;
    if (!htmlFileUrl) {
      throw new Error("[version-update-polling]: htmlFileUrl is required");
    }

    const response = await fetch(htmlFileUrl, {
      method: "HEAD",
      cache: "no-cache"
    })

    if (Number(response.status) !== 200) {
      throw new Error(`[version-update-polling]: status is ${response.status}`);
    }

    const etag = response.headers.get("etag");
    if (etag == null) {
      throw new Error(`[version-update-polling]: etag is null`)
    }

    this.appEtag = etag;
    localStorage.setItem(this.options.appETagKey, etag);

    this.start();
  }

  /**
   *  start() 1)开始轮询任务，创建Web Worker并发送启动消息，设置Web Worker的消息监听器，当检测到ETag变化时调用onUpdate回调函数。
   * 2)同时处理页面可见性事件。如果配置项中silent为true，则不会开始轮询和监听页面可见性。
   */
  start() {
    const { appETagKey, pollingInterval, htmlFileUrl, silent, silentPollingInterval, silentPageVisibility } = this.options;
    if (silent) {
      return;
    }

    myWorker = createWorker(createWorkerFunc);
    myWorker.postMessage({
      code: "start",
      data: {
        appETagKey,
        pollingInterval,
        htmlFileUrl,
        silentPollingInterval,
        lastEtag: this.appEtag
      }
    })
    // Web Worker监听主线程的消息，如果Etag变化，更新时的回调onUpdate()
    myWorker.onmessage = (event) => {
      const { lastEtag, etag } = event.data;
      if (lastEtag !== etag) {
        // 停止轮询
        this.stop();
        // 给外界调用的onUpdate()
        this.options.onUpdate?.(this)
      }
    }

    if (!silentPageVisibility) {
      if (!attached) {
        document.addEventListener("visibilitychange", handleVisibilityChange);
        /* document.addEventListener("visibilitychange", (event) => {
          console.log(event.target.visibilityState, '>>>visibilityState', event.target.hidden, '===hidden');
          handleVisibilityChange()
        }); */
        attached = true;
      }
    }
  }

  /**
   *  stop()停止轮询任务，关闭Web Worker并移除visibilitychange页面可见性改变的事件监听器。
   */
  stop() {
    if (myWorker) {
      closeWorker(myWorker);
      if (attached) {
        // console.log('停止轮询，移除visibilitychange', attached, '<<attached');
        document.removeEventListener("visibilitychange", handleVisibilityChange);
        attached = false;
      }
    }
  }

  // onRefresh()方法刷新页面。可供self调用。
  onRefresh() {
    window.location.reload();
  }


  // onCancel方法当配置项中强制更新forceUpdate为true时重新开始轮询。可供self调用。
  onCancel() {
    this.options.forceUpdate && this.start();
  }
}


// createVersionPolling工厂函数,创建并返回一个新的VersionPolling实例。
export function createVersionPolling(options) {
  const versionPolling = new VersionPolling(options);
  return versionPolling;
}

